package ru.usu.gv.gui.actions;

import ru.usu.gv.core.GraphCanvas;
import ru.usu.gv.uicomponents.IUIVertexType;
import ru.usu.gv.uicomponents.UIGraph;
import ru.usu.gv.uicomponents.UIVertex;
import ru.usu.gv.uicomponents.UIVertexTypeRegistry;
import ru.usu.gv.utils.Pair;
import ru.usu.gv.utils.RandomUtilities;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @author spupyrev
 * 29.10.2008
 */
public class ColorizeCommunitiesAction extends ActionBase
{
	private Map<UIVertex, Integer> vertexToCluster;
	private boolean changeLabel;

	public ColorizeCommunitiesAction(Map<UIVertex, Integer> vertexToCluster, boolean changeLabel)
	{
		this.vertexToCluster = vertexToCluster;
		this.changeLabel = changeLabel;
	}

	public ColorizeCommunitiesAction(Map<UIVertex, Integer> vertexToCluster)
	{
		this(vertexToCluster, false);
	}

	@Override
	public void execute()
	{
		UIGraph uiGraph = GraphCanvas.getInstance().getCurrentGraph();

		List<Pair<Color, IUIVertexType>> colors = generateColors(uiGraph);

		for (UIVertex v : uiGraph.getVertexes())
		{
			Integer cluster = vertexToCluster.get(v);

			if (cluster == null)
			{
				System.err.println("No cluster for vertex " + v);
				continue;
			}

			Color color = colors.get(cluster).getFirst();
			v.getViewItem().setColor(color);

			IUIVertexType newVertexType = colors.get(cluster).getSecond();
			v.changeVertexType(newVertexType);

			if (changeLabel)
			{
				v.setLabel(String.valueOf(cluster));
			}
		}

		//System.out.println("Different clusters: " + colors.size());
		uiGraph.draw();
	}

	private List<Pair<Color, IUIVertexType>> generateColors(UIGraph uiGraph)
	{
		List<Pair<Color, IUIVertexType>> colors = new ArrayList();

		Map<Integer, Integer> clusterCount = new TreeMap();
		for (UIVertex v : uiGraph.getVertexes())
		{
			Integer cluster = vertexToCluster.get(v);
			if (clusterCount.containsKey(cluster))
				clusterCount.put(cluster, clusterCount.get(cluster) + 1);
			else
				clusterCount.put(cluster, 1);
		}

		int count = clusterCount.size();
		if (count <= 1)
			return Collections.singletonList(new Pair<Color, IUIVertexType>(Color.black, generateRandomVertexType(0)));

		for (int i = 0; i < count; i++)
			colors.add(new Pair(Color.black, generateRandomVertexType(i)));

		Set<Integer> available = new HashSet<Integer>();
		Set<Integer> used = new HashSet<Integer>();
		int cur = 0;
		int delta = 200 / (count - 1);
		for (int i = 0; i < count; i++)
		{
			available.add(cur);
			cur += delta;
		}

		List<Pair<Integer, Integer>> pairs = new ArrayList();
		for (Integer key : clusterCount.keySet())
			pairs.add(new Pair(clusterCount.get(key), key));

		Collections.sort(pairs, new Comparator<Pair<Integer, Integer>>()
		{
			public int compare(Pair<Integer, Integer> o1, Pair<Integer, Integer> o2)
			{
				if (o1.getFirst() > o2.getFirst())
					return -1;
				else if (o1.getFirst() < o2.getFirst())
					return 1;
				else if (o1.getSecond() > o2.getSecond())
					return 1;
				else if (o1.getSecond() < o2.getSecond())
					return -1;
				else
					return 0;
			}
		});

		Set<Color> usedColors = new HashSet();
		usedColors.add(Color.white);
		for (int i = 0; i < pairs.size(); i++)
		{
			Pair<Integer, Integer> pair = pairs.get(i);
			//bw:
			//int colorComponent = getFurthest(available, used);
			//colors.get(pair.getSecond()).setFirst(new Color(colorComponent, colorComponent, colorComponent));
			//color:
			Color newColor = getFurthestColor(usedColors);
			usedColors.add(newColor);
			colors.get(pair.getSecond()).setFirst(newColor);
			colors.get(pair.getSecond()).setSecond(generateRandomVertexType(i));

			//available.remove(colorComponent);
			//used.add(colorComponent);
		}

		return colors;
	}

	private Color getFurthestColor(Set<Color> used)
	{
		if (used.isEmpty())
			return Color.black;
		Color bestColor = null;
		double bestDist = -1;
		for (int t = 0; t < 1000; t++)
		{
			Color color = new Color(RandomUtilities.nextInt());
			double dist = calculateDistance(color, used);
			if (bestDist == -1 || dist > bestDist)
			{
				bestDist = dist;
				bestColor = color;
			}
		}

		//System.out.println("Dist: " + bestDist);
		return bestColor;
	}

	private int getFurthest(Set<Integer> available, Set<Integer> used)
	{
		int bestDist = -1, result = -1;
		for (Integer key : available)
		{
			int curDist = calculateDistance(key, used);
			if (curDist > bestDist)
			{
				bestDist = curDist;
				result = key;
			}
		}

		assert (result != -1);
		return result;
	}

	private int calculateDistance(Integer key, Set<Integer> used)
	{
		int result = 0;
		for (Integer i : used)
			result += Math.abs(key - i);

		return result;
	}

	private double calculateDistance(Color current, Set<Color> used)
	{
		double dist = Double.MAX_VALUE;
		for (Color i : used)
		{
			dist = Math.min(dist, calculateDistance(current, i));
		}

		return dist;
	}

	private double calculateDistance(Color c1, Color c2)
	{
		double rmean = (c1.getRed() + c2.getRed()) / 2.0;
		double r = c1.getRed() - c2.getRed();
		double g = c1.getGreen() - c2.getGreen();
		double b = c1.getBlue() - c2.getBlue();

		r /= 255.0;
		g /= 255.0;
		b /= 255.0;
		rmean /= 255.0;

		double res = (rmean + 2.0) * r * r + 4 * g * g + (2.0 + (1.0 - rmean)) * b * b;
		assert (res >= -1e-6);
		return Math.sqrt(res);
	}

	private IUIVertexType generateRandomVertexType(int cluster)
	{
		List<String> vertexTypeIds = UIVertexTypeRegistry.getVertexTypeIds();
		vertexTypeIds.remove("lodsphere");
		vertexTypeIds.remove("cylinder");
		cluster = cluster % vertexTypeIds.size();
		return UIVertexTypeRegistry.findVertexType(vertexTypeIds.get(cluster));
	}

}
